package com.nulldev.util.threading.legacy.dbg;

import java.lang.management.ThreadInfo;
import java.util.ArrayList;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collections;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.lang.management.ThreadMXBean;

public class ThreadDataCollector {
	private ThreadMXBean threadMXBean;
	private int updateCount;
	private boolean debug;
	private boolean includeThreadPriorities;
	private Map<Long, ThreadInfoStats> threadData;
	private ProcessInfoStats processInfo;
	private long startMeasurement;
	private int cpuUsagePercentage;
	private int threadsBlocked;
	private int threadsRunning;
	private int threadsTimedWaiting;
	private int threadsWaiting;

	public ThreadDataCollector(final ThreadMXBean threadMXBean, final boolean debug) {
		this.threadData = new HashMap<Long, ThreadInfoStats>();
		this.processInfo = new ProcessInfoStats("Process (% CPU of all threads together)");
		this.threadMXBean = threadMXBean;
		this.debug = debug;
	}

	public synchronized <K> List<InfoStats> getThreadList(final boolean b) throws SecurityException {
		if (this.threadMXBean == null) {
			return Collections.emptyList();
		}
		++this.updateCount;
		long[] allThreadIds;
		ThreadInfo[] threadInfo;
		try {
			allThreadIds = this.threadMXBean.getAllThreadIds();
			threadInfo = this.threadMXBean.getThreadInfo(allThreadIds);
		} catch (UndeclaredThrowableException ex) {
			if (this.debug) {
				System.err.println("error while getting thread info: " + ex + " caused by " + ex.getCause());
			}
			return Collections.emptyList();
		} catch (SecurityException ex2) {
			throw ex2;
		} catch (Exception ex3) {
			if (this.debug) {
				System.err.println("error while getting thread info: " + ex3);
				ex3.printStackTrace();
			}
			return Collections.emptyList();
		}
		Object determineThreadPriorities = null;
		if (this.includeThreadPriorities) {
			determineThreadPriorities = this.determineThreadPriorities();
		}
		final long currentTimeMillis = System.currentTimeMillis();
		long n = 0L;
		for (int i = 0; i < allThreadIds.length; ++i) {
			assert threadInfo[i].getThreadId() == allThreadIds[i];
			final long n2 = allThreadIds[i];
			if (threadInfo[i] != null) {
				final long threadCpuTime = this.threadMXBean.getThreadCpuTime(n2);
				if (threadCpuTime != -1L) {
					ThreadInfoStats threadInfoStats = this.threadData.get(n2);
					if (threadInfoStats == null) {
						threadInfoStats = new ThreadInfoStats(n2, threadInfo[i], threadCpuTime);
						this.threadData.put(n2, threadInfoStats);
					} else {
						n += threadInfoStats.update(threadInfo[i], threadCpuTime);
					}
					if (this.includeThreadPriorities) {
						final Integer threadPriority = ((Map<K, Integer>) determineThreadPriorities).get(n2);
						if (threadPriority != null) {
							threadInfoStats.setThreadPriority(threadPriority);
						} else {
							System.err.println("no prio for thread " + n2);
						}
					}
				}
			}
		}
		final long currentTimeMillis2 = System.currentTimeMillis();
		final long startMeasurement = this.startMeasurement;
		this.startMeasurement = currentTimeMillis + (currentTimeMillis2 - currentTimeMillis) / 2L;
		if (startMeasurement != 0L) {
			final long n3 = this.startMeasurement - startMeasurement;
			final long n4 = 1000L * (n / 1000L) / n3;
			this.cpuUsagePercentage = (int) (100L * n4 / 1000000L);
			if (this.debug) {
				System.out.println(String.format(
						"Obtaining cputimes took %3d ms, measurement period was %d ms, current total (/s) = %d",
						currentTimeMillis2 - currentTimeMillis, n3, n4));
			}
		}
		this.resetStateCounts();
		final ArrayList<InfoStats> list = (ArrayList<InfoStats>) new ArrayList<InfoStats>(this.threadData.values());
		for (final InfoStats threadInfoStats2 : list) {
			if (!(threadInfoStats2 instanceof ThreadInfoStats)) {
				continue;
			}
			final ThreadInfoStats tr = (ThreadInfoStats) threadInfoStats2;
			if (!tr.checkUpdate(this.updateCount)
					&& this.threadMXBean.getThreadInfo(threadInfoStats2.getId()) == null) {
				this.threadData.remove(threadInfoStats2.getId());
			}
			tr.computePercentage(n);
			this.updateStateCounts(threadInfoStats2.getState());
		}
		final List<InfoStats> list2 = (List<InfoStats>) list;
		if (b) {
			this.processInfo.update(n);
			this.processInfo.setPercentage(this.cpuUsagePercentage);
			list2.add((InfoStats) this.processInfo);
		}
		return (List<InfoStats>) list2;
	}

	public Map<Thread.State, Integer> getStats() {
		final HashMap<Thread.State, Integer> hashMap = new HashMap<Thread.State, Integer>();
		hashMap.put(Thread.State.BLOCKED, this.threadsBlocked);
		hashMap.put(Thread.State.RUNNABLE, this.threadsRunning);
		hashMap.put(Thread.State.TIMED_WAITING, this.threadsTimedWaiting);
		hashMap.put(Thread.State.WAITING, this.threadsWaiting);
		return hashMap;
	}

	public int getCpuUsagePercentage() {
		return this.cpuUsagePercentage;
	}

	private void resetStateCounts() {
		this.threadsBlocked = 0;
		this.threadsRunning = 0;
		this.threadsTimedWaiting = 0;
		this.threadsWaiting = 0;
	}

	private void updateStateCounts(final Thread.State state) {
		switch (state) {
			case BLOCKED : {
				++this.threadsBlocked;
				break;
			}
			case RUNNABLE : {
				++this.threadsRunning;
				break;
			}
			case TIMED_WAITING : {
				++this.threadsTimedWaiting;
				break;
			}
			case WAITING : {
				++this.threadsWaiting;
				break;
			}
		}
	}

	private Map<Long, Integer> determineThreadPriorities() {
		return Collections.emptyMap();
	}

	public synchronized void dispose() {
		this.threadMXBean = null;
		this.threadData = null;
	}
}